﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;

namespace HZY.Framework.SourceGenerator;

[Generator]
public class ScanScheduledAttributeSourceGenerator : ISourceGenerator
{
    public void Initialize(GeneratorInitializationContext context)
    {
        // throw new NotImplementedException();
        Debugger.Launch();
        // context.RegisterForSyntaxNotifications(() => new SyntaxReceiver());
    }

    public void Execute(GeneratorExecutionContext context)
    {
        Debugger.Launch();
        
        // throw new NotImplementedException();
        // 扫描所有函数带有 [ScheduledAttribute] 标记的
        var assembly = context.Compilation.Assembly;
        if (context.SyntaxReceiver is not SyntaxReceiver receiver)
            return;

        var methods = receiver.CandidateMethods;

        if (!methods.Any())
            return;

        var methodInfos = new List<(string methodName, string className, string cronExpression)>();

        foreach (var method in methods)
        {
            var methodSymbol =
                ModelExtensions.GetDeclaredSymbol(context.Compilation.GetSemanticModel(method.SyntaxTree), method,
                    context.CancellationToken);
            var attributeData = methodSymbol.GetAttributes()
                .FirstOrDefault(ad => ad.AttributeClass.Name == "ScheduledAttribute");

            if (attributeData == null)
                continue;

            var cronExpression = attributeData.ConstructorArguments[0].Value.ToString();
            methodInfos.Add((method.Identifier.Text, methodSymbol.ContainingType.Name, cronExpression));
        }

        if (!methodInfos.Any())
            return;

        var source = GenerateSource(methodInfos);
        context.AddSource("ScheduledMethods.g.cs", SourceText.From(source, Encoding.UTF8));
    }

    private string GenerateSource(List<(string methodName, string className, string cronExpression)> methodInfos)
    {
        var sb = new StringBuilder(@"
using System;
using System.Collections.Generic;

public static class ScheduledMethods
{
    public static readonly List<ScheduledMethodInfo> MethodInfos = new List<ScheduledMethodInfo>
    {
");

        foreach (var info in methodInfos)
        {
            sb.AppendLine(
                $"        new ScheduledMethodInfo(\"{info.className}\", \"{info.methodName}\", \"{info.cronExpression}\"),");
        }

        sb.Append(@"
    };
}

public class ScheduledMethodInfo
{
    public string ClassName { get; }
    public string MethodName { get; }
    public string CronExpression { get; }

    public ScheduledMethodInfo(string className, string methodName, string cronExpression)
    {
        ClassName = className;
        MethodName = methodName;
        CronExpression = cronExpression;
    }
}
");

        return sb.ToString();
    }

    private class SyntaxReceiver : ISyntaxReceiver
    {
        public List<MethodDeclarationSyntax> CandidateMethods { get; } = new();

        public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
        {
            if (syntaxNode is MethodDeclarationSyntax methodDeclaration &&
                methodDeclaration.AttributeLists.Any())
            {
                CandidateMethods.Add(methodDeclaration);
            }
        }
    }
}